﻿using Engine.Entities.Character;
using FrenzyIsland.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace Game.Scripts.Managers {
    [Serializable]
    public class Building : MonoBehaviour {

        public enum EnumLevelUpError
        {
            Undefined, 
            OK,
            NoResource,
            NoLevelAvaible
        }

        internal BuildingPrototype _prototype;
        internal uint _level;

        private List<SteveBrain> _workers;
        private GameObject uiWidget;

        //ultimo tempo di produzione
        private float lastTime;

        //magazzino che tiene traccia delle risorse immagazzinate dall'ultima raccolta
        public int WarehouseResource {
            get
            {
                return (int)warehouseResource;
            }
        }
        private float warehouseResource = 0;

        //fattore moltiplicativo per la capienza del magazzino
        //N.B. Capienza magazzino = GetEstimateResourceProductionPerMinute x factorMinuteCapability
        private float factorMinuteCapability = 10;

        public SteveBrain[] Workers
        {
            get
            {
                return _workers.ToArray<SteveBrain>();
            }
        }

        public void Start() { 
            this._workers = new List<SteveBrain>();
            this.lastTime = Time.time;
        }

        public void Update() {

            float deltaTimeSecond = Time.time - this.lastTime;
            int limitWarehouse = GetMaxCapability();

            //converto la produzione in minuti
            float resourseProduction = (deltaTimeSecond / 60) * GetResourceProductionPerMinute();

            //decido se prendere o buttare le risorse perchè il magazzino è pieno
            if (warehouseResource + resourseProduction > limitWarehouse)
                warehouseResource = limitWarehouse;
            else
                warehouseResource += resourseProduction;

            //mostro il pulsante di raccolta se il magazzino è pieno almeno al 10%
            //il 10% in minuti equivale a factorMinuteCapability / 10
            if( warehouseResource / limitWarehouse > 0.01f )
            {
                if (this.uiWidget == null)
                {
                    GuiManager guiManager = UIRoot.list[0].GetComponent<GuiManager>();
                    GameObject prefab = BuildingManager.instance.resourceCollectedWidget;
                    GuiManager.TypePlaceHolders type = GuiManager.TypePlaceHolders.Building;

                    this.uiWidget = guiManager.AddFollowTarget(type, prefab, this.gameObject);
                  
                    //imposto nome e building
                    this.uiWidget.name = this.name;
                    this.uiWidget.GetComponent<ResourceCollectedController>().building = this;

                    //imposto l'evento sul click del widget per raccogliere le risorse
                    UIButton clicker = this.uiWidget.GetComponentInChildren<UIButton>();
                    clicker.onClick.Add(new EventDelegate(this, "OnDepositResourceCollected"));
                }
            }
            this.lastTime = Time.time;
        }

        /// <summary>
        /// - N 	: Numero di persone che può ospitare la struttura
        /// - P		:= 1   produzione di una risorsa da parte di un personaggio di livello 1
        /// - S		: Media della skill usata dall'edificio di tutti i lavoratori dello stesso
        /// - μL	:= 1.1 (bonus di livello)
        /// - μS 	:= 1.1 (bonus di Skill)
        /// - LV	: Livello dell'edificio
        /// Si ottiene:
        /// risorse / minuto = N * P +  ((LV-1) * μL)^2  + (S - 1) * μS
        /// </summary>
        internal double GetEstimateResourceProductionPerMinute(int livello)
        {
            int N = (int)prototype.workersPerLevel * livello;
            int P = 1;

            double S = 0;
            if (workersCount == 0)
                S = 1;
            else   
                S = (from SteveBrain item in Workers
                            select item.model.Skills.ValueSkill[prototype.preferredSkill]
                            ).Average();

            double μL = 1.1;
            double μS = 1.1;
            double LV = level;

            double ris = N * P + Math.Pow((LV - 1) * μL, 2) + (S - 1) * μS;

            return ris;

        }

        //steffa formula di sopra, ma non avere lavoratori comporta una produzione di 0
        internal float GetResourceProductionPerMinute()
        {
            int N = (int)prototype.workersPerLevel * (int)level;
            int P = 1;

            if (workersCount == 0) return 0;
            
            double S = (from SteveBrain item in Workers
                        select item.model.Skills.ValueSkill[prototype.preferredSkill]
                        ).Average();

            double μL = 1.1;
            double μS = 1.1;
            double LV = level;

            float ris = (float)(N * P + Math.Pow((LV - 1) * μL, 2) + (S - 1) * μS);

            return ris;

        }

        /// <summary>
        /// La storage massimo di un edificio è dato dalla:
        /// Produzione al minuto * 60 minuti
        /// </summary>
        /// <returns></returns>
        public int GetMaxCapability()
        {
            return (int)( GetEstimateResourceProductionPerMinute( (int)level ) * factorMinuteCapability );
        }
        
        /// <summary>
        /// Evento di click sulla spunta
        /// </summary>
        public void OnDepositResourceCollected() {

            ResourceManager manager = ResourceManager.instance;
            Resource resourceToProduce = this.prototype.resourceProduced;

            if (manager != null) {
                Resource resourceDeposited = manager.getResourceByType(resourceToProduce.type);
                if (resourceDeposited != null) {
                    resourceDeposited.deposit( (uint)warehouseResource );
                }
            }

            //azzero il magazzino
            warehouseResource = 0;

            DestroyImmediate(this.uiWidget);
        }

        /// <summary>
        /// Level up e pago le risorse
        /// false: nessun problema
        /// true:  errore
        /// </summary>
        internal bool levelUP()
        {
            //controllo se posso fare level up
            if ( CanLevelUP() != EnumLevelUpError.OK) return true;

            //pago le risorse
            Resource[] resources = prototype.GetListCost((int)level + 1);
            ResourceManager.instance.withdraw(resources);

            _level++;
            
            return false;
        }

        /// <summary>
        /// Effettua dei test sulla possibilità di effettuare un upgrade dell'eddificio
        /// </summary>
        public EnumLevelUpError CanLevelUP()
        {
            //verifico che esista un possibile livello successivo
            Resource[] resources = prototype.GetListCost((int)level + 1);
            if (resources == null) return EnumLevelUpError.NoLevelAvaible;

            //chiedo se il Resourcemanager ha abbastanza risorse
            bool haveAmounts = ResourceManager.instance.hasAmounts(resources);
            if (!haveAmounts) return EnumLevelUpError.NoResource;

            return EnumLevelUpError.OK;
        }

        public BuildingPrototype prototype {
            get {
                return this._prototype;
            }
        }

        public uint level {
            get {
                return this._level;
            }
        }

        public uint workersCount {
            get {
                return (uint)this._workers.Count;
            }
        }

        public bool isFull {
            get{
                uint maxWorkers = this._level * this.prototype.workersPerLevel;
                return this.workersCount == maxWorkers;
            }
        }

        public void addWorker(SteveBrain worker){
            if (!this.isFull && worker!=null) {
                this._workers.Add(worker);
                worker.workplace = this;
            }
        }

        public void removeWorker(SteveBrain worker) {
            if (worker != null) {
            this._workers.Remove(worker);
                worker.workplace = null;
            }
        }
    }
}
